前情提要:
昨天暫時把todos告一段落,今天要開始新建news內容,計畫很簡單就是使用News API取得新聞內容。
剛好這個API很貼心可以選擇國家,所以就想說透過這個API可以看個各國新聞,這次訂定了台灣、日本和美國當作選項
剛好有機會再使用到middleware,這次就使用redux-thunk處理吧。
redux-thunk安裝設定
yarn add redux-thunk
安裝完後再次打開store/index.js,接著依照redux-thunk的步驟匯入applyMiddleware和redux-thunk。接著在createStore設定使用thunk,這樣就設定完成囉!
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers";
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
export default store;
redux部分相關設定
接著是在reducers目錄下新增news_reducer.js處理有關news的state,初始化state的資料內預計會有articles和country,儲存新聞資料和選擇哪個國家的新聞,分別設定初始值為陣列與空字串。
export default function(state = {
articles: [],
country: ""
}, action) {
// ......
return state
}
在reducers/index.js也別忘了在combineReducers匯入newsReducer呦!
// ...
import newsReducer from './news_reducer';
const rootReducer = combineReducers({
todoList: todoListReducer,
news: newsReducer
});
// ...
前置作業完成後就可以寫UI了
UI部分拆成三部分處理,第一個是選擇新聞來源國家的NewsSelect,再來是新聞清單的NewsList,接著是渲染每則新聞的NewsListItem,這三個component都屬於Presentational component。透過最外層的Container component(News component)和redux溝通。
傳入NewsSelect的props分別為country和fetchNews,會將country存在store的原因是因為希望切換route後再回來能保留value。
<NewsSelect fetchNews={fetchNews} country={country} />
透過redux-thunk處理fetchNews。一般的action都是直接return object,而透過redux-thunk可以return function,而function內會收到dispatch
和getState
作為參數,等收到object後再交給reducer處理。
fetchNews這裡用了三個dispatch。第一次用在更新isLoading和country state,收到response後再依據success或fail發送不同action
export const fetchNews = country => {
const URL = `https://newsapi.org/v2/top-headlines?country=${country}&apiKey=bb126a5312f5417c852cd93ce738a8f2`;
return dispatch => {
dispatch({ type: FETCH_NEWS_REQUEST, country });
axios
.get(URL)
.then(res => dispatch({ type: FETCH_NEWS_SUCCESS, res }))
.catch(err => dispatch({ type: FETCH_NEWS_ERROR, err }));
}
};
在news_reducer.js這邊也依據action type做不同處理
switch (action.type) {
case FETCH_NEWS_SUCCESS:
let { data:{articles}, status } = action.res;
return status === 200 ? { ...state, articles, isLoading: false} : state;
case FETCH_NEWS_ERROR:
return { err: 'Something Wrong...', isLoading: false }
case FETCH_NEWS_REQUEST:
return { ...state, isLoading: true, country: action.country }
default:
return state;
}
收到state的News component再透過props將資料傳給NewsList,若收到err則顯示錯誤訊息,下面會看到props除了acticles外還有isLoading和pageUrl也送進去,isLoading就是之前做過的簡單HOC,只是這邊用了react-fontawesome做Loading顯示,另一個pathname是因為之後Link component有需要使用所以先丟進去,但寫到這裡又覺得不用這麼麻煩也行,pathname的部分可能我還需要再想想...
// ...
const News = ({ err, articles, fetchNews, country, isLoading, location }) => {
return (
<main className="main">
<NewsSelect fetchNews={fetchNews} country={country} />
{err ? (
<div>{err}</div>
) : (
<NewsList
articles={articles}
isLoading={isLoading}
pathname={location.pathname}
/>
)}
</main>
);
};
// ...
hoc/with_loading.js
// ...
const withLoading = (WrappedComponent) => ({isLoading, ...props}) => {
return isLoading ?
<div className="loading-block">
<FontAwesomeIcon icon={faSpinner} size="2x" color="#777" pulse />
</div> :
<WrappedComponent {...props} />;
};
export default withLoading;
之後就只剩render NewsList和NewsListItem要處理了!覺得畫面UI處理因人而異,就不另述了~
明天會再詳述各則新聞內容顯示處理。只能說,一開始以為能收到完整新聞資料的我真的是太蠢了!
今天處理的部份操作畫面如下:
明天再繼續寫內頁部分!